package com.crois.barrier.server.init;

import com.crois.barrier.common.spi.ServiceLoaderUtil;
import com.crois.barrier.server.utils.NetUtils;
import com.crois.barrier.transport.transport.init.InitFunc;
import com.crois.barrier.transport.transport.init.InitOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicBoolean;


/**
 * @author Hou Ze Yu
 * @description
 * @date 2020/10/23
 */
@Component
public class InitBarrierHeartbeatExecutor implements ApplicationListener<WebServerInitializedEvent> {

    @Value("${barrier.admin.server:127.0.0.1:9999}")
    private String adminServer;
    @Value("${barrier.admin.enabled:true}")
    private Boolean adminEnabled;
    @Value("${spring.application.name}")
    private String serverName;

    public String getAdminServer() {
        return adminServer;
    }

    public Boolean getAdminEnabled() {
        return adminEnabled;
    }



    public InitBarrierHeartbeatExecutor setAdminEnabled(Boolean adminEnabled) {
        this.adminEnabled = adminEnabled;
        return this;
    }

    public InitBarrierHeartbeatExecutor setAdminServer(String adminServer) {
        this.adminServer = adminServer;
        return this;
    }
    private static AtomicBoolean initialized = new AtomicBoolean(false);

    private static Logger LOGGER = LoggerFactory.getLogger(InitBarrierHeartbeatExecutor.class);

    @Override
    public void onApplicationEvent(WebServerInitializedEvent webServerInitializedEvent) {
        doInit(webServerInitializedEvent.getWebServer().getPort());
    }


    public  void doInit(int port) {
        //  如果 配置 开启了 管理端 才向 管理端 发送心跳
        if (Boolean.TRUE.equals(adminEnabled)){
            if (!initialized.compareAndSet(false, true)) {
                return;
            }
            try {
                ServiceLoader<InitFunc> loader = ServiceLoaderUtil.getServiceLoader(InitFunc.class);
                List<OrderWrapper> initList = new ArrayList<OrderWrapper>();

                for (InitFunc initFunc : loader) {
                    LOGGER.debug("[InitExecutor] Found init func: " + initFunc.getClass().getCanonicalName());
                    insertSorted(initList, initFunc);
                }
                for (OrderWrapper w : initList) {
                    w.func.init(serverName,adminServer, NetUtils.getLocalAddress() ,port);
                    LOGGER.debug(String.format("[InitExecutor] Executing %s with order %d",
                            w.func.getClass().getCanonicalName(), w.order));
                }
            } catch (Exception ex) {
                LOGGER.warn("[InitExecutor] WARN: Initialization failed", ex);
                ex.printStackTrace();
            } catch (Error error) {
                LOGGER.warn("[InitExecutor] ERROR: Initialization failed with fatal error", error);
                error.printStackTrace();
            }
        }
    }


    private static void insertSorted(List<OrderWrapper> list, InitFunc func) {
        int order = resolveOrder(func);
        int idx = 0;
        for (; idx < list.size(); idx++) {
            if (list.get(idx).getOrder() > order) {
                break;
            }
        }
        list.add(idx, new OrderWrapper(order, func));
    }

    private static int resolveOrder(InitFunc func) {
        if (!func.getClass().isAnnotationPresent(InitOrder.class)) {
            return InitOrder.LOWEST_PRECEDENCE;
        } else {
            return func.getClass().getAnnotation(InitOrder.class).value();
        }
    }


    private static class OrderWrapper {
        private final int order;
        private final InitFunc func;

        OrderWrapper(int order, InitFunc func) {
            this.order = order;
            this.func = func;
        }

        int getOrder() {
            return order;
        }

        InitFunc getFunc() {
            return func;
        }
    }


}
